package midend.IR_Value;

import Utils.LibFunc;
import midend.CFG_Builder;
import midend.IR_Instruction.*;
import midend.IR_Type.IR_Float;
import midend.IR_Type.IR_Func;
import midend.IR_Type.IR_Type;
import midend.IR_Module;
import midend.IR_Type.*;
import midend.IR_Type.IR_Float;
import midend.IR_Type.IR_Func;
import midend.IR_Type.IR_Type;
import midend.Loop;
import midend.IR_Module;
import midend.IR_Type.*;

import java.util.*;
import java.util.HashMap;

public class IR_FuncBlock extends IR_Value{
    private LinkedList<IR_BaseBlock> bbList;
    private IR_Func Functype;
    private List<IR_Value> params;
    private IR_BaseBlock exitBlock = null;
    private String FuncName;
    private int canGVN = -1;
    //private List<IR_BaseBlock> ret_List;
    //该函数里面调用的函数
    private List<IR_FuncBlock> calledlist = new ArrayList<>();
    //调用该函数的函数
    private List<IR_FuncBlock> becalledlist = new ArrayList<>();

    private boolean sideEffect = false;
    private boolean isTail = false;
    private boolean isMem = false;

    private IR_Value retValue;

    //private ArrayList<IR_FuncBlock> beCalledList = new ArrayList<>(); //调用该函数的函数
    //private ArrayList<IR_FuncBlock> callList = new ArrayList<>(); //该函数调用的函数

    public static HashMap<IR_Value, IR_Value> cloneMap_Value;
    public static HashMap<Instruction, Instruction> cloneMap_Instruction;

    private HashMap<IR_BaseBlock, ArrayList<IR_BaseBlock>> preMap = new HashMap<>();
    private HashMap<IR_BaseBlock, ArrayList<IR_BaseBlock>> nextMap = new HashMap<>();
    //循环分析相关
    // top level loop
    private Set<Loop> topLevelLoops = new HashSet<>();
    // 每个基本块所属的最内层循环
    private HashMap<IR_BaseBlock,Loop> loopInfo = new HashMap<>();
    // 所有循环
    private Set<Loop> Loops = new HashSet<>();


    public IR_FuncBlock(IR_Func functype, String funcName) {
        Functype = functype;
        FuncName = funcName;
        this.bbList = new LinkedList<>();
        this.params = new ArrayList<>();
        //初始化参数列表
        int count = 0;
        for (IR_Type paramType : Functype.get_Params_Type()) {
            IR_Value val = new IR_Local_Value(funcName + count, paramType);
            count++;
            params.add(val);
        }
    }

    public void addbb(IR_BaseBlock bb){
        this.bbList.add(bb);
    }

    public String get_FuncName() {
        return FuncName;
    }

    public LinkedList<IR_BaseBlock> get_bb_List(){
        return bbList;
    }
    public IR_Type get_RetType(){
        return this.Functype.get_Return_Type();
    }
    public List<IR_Value> get_params() {
        return params;
    }

    public void setParams(List<IR_Value> params) {
        this.params = params;
    }

    public IR_Value get_param(int i) {
        return params.get(i);
    }

    public void add_Param(IR_Value param) {
        this.params.add(param);
    }

    @Override
    public String get_ValueName() {
        return "@" + FuncName;
    }

    @Override
    public IR_Type get_Type() {
        return Functype;
    }

    @Override
    public int get_Const_Type() {
        return Functype.get_Const_Type();
    }

    //形如 define i32 @func(i32 %func0, i32 %func1)
    public String code_Gen(){
        //函数声明的IR代码
        StringBuilder s = new StringBuilder();
        //函数定义的时候  声明函数以及返回类型
        s.append("define ").append(get_RetType().get_TypeName()).append(" ").append(get_ValueName());
        //添加传递的参数
        s.append('(');
        int count = 0;
        for(IR_Value para:params){
            if(count>=1) {
                s.append(", ");
            }
            count++;
            s.append(para.get_Type().get_TypeName()).append(" ").append(para.get_ValueName());
        }
        s.append(')').append("{\n");
        for(IR_BaseBlock bb:bbList) {
            s.append(bb.get_ValueName()).append(":\n");
            //把这个 bb 里面的instruction的中间代码直接导入
            bb.setS();
            for (Instruction instruction : bb.get_Instruction_List()) {
                if (instruction instanceof Alloc_Instrction || instruction instanceof Store_Instruction) {
                    bb.appendln(instruction.toString(), 4);
                } else {
                    bb.appendln(instruction.toString());
                }
            }
            s.append(bb.get_s());
        }

        s.append("}\n");
        return s.toString();
    }

    public void setCalledlist(List<IR_FuncBlock> calledlist) {
        this.calledlist = calledlist;
    }

    public void setBecalledlist(List<IR_FuncBlock> becalledlist) {
        this.becalledlist = becalledlist;
    }

    //是否存在递归调用
    public boolean isRecursive(){
        return this.calledlist.contains(this);
    }

    public boolean isTailRecursive(){
        return this.isTail;
    }

    public void setTail() {
        isTail = true;
    }

    public void setSideEffect() {
        this.sideEffect = true;
    }

    public boolean isSideEffect() {
        return this.sideEffect;
    }

    public boolean isLib() {
        LibFunc.initLib();
        for(IR_FuncBlock libFun : LibFunc.libFunc) {
            if (("@"+FuncName).equals(libFun.get_FuncName()) || ("@"+"@"+FuncName).equals(libFun.get_FuncName())){
                this.setSideEffect();
                return true;
            }
        }
        return false;
    }

    public void setExitBlock(IR_BaseBlock block) {
        this.exitBlock = block;
    }

    public IR_BaseBlock getExitBlock() {
        return this.exitBlock;
    }

    public ArrayList<IR_Local_Value> getFParams() {
        ArrayList<IR_Local_Value> fParams = new ArrayList<>();
        for (IR_Value param : params) {
            if (param.get_Type() instanceof IR_Float) {
                fParams.add((IR_Local_Value)param);
            }
        }
        return fParams;
    }

    public ArrayList<IR_Local_Value> getIParams() {
        ArrayList<IR_Local_Value> IParams = new ArrayList<>();
        for (IR_Value param : params) {
            if (!(param.get_Type() instanceof IR_Float)) {
                IParams.add((IR_Local_Value)param);
            }
        }
        return IParams;
    }

    public boolean canBeGVN() {
        if (canGVN != -1) {
            return canGVN == 1;
        }
        for (IR_Value param : params) {
            String type_name = param.get_Type().get_TypeName();
            if (type_name.charAt(type_name.length() - 1) == '*') {
                canGVN = 0;
                return false;
            }
        }
        for (IR_BaseBlock block : bbList) {
            for (Instruction instruction : block.get_Instruction_List()) {
                if (instruction instanceof Call_Instruction) {
                    canGVN = 0;
                    return false;
                }
                for (IR_Value value : instruction.get_op()) {
                    if (value instanceof IR_Global_Value) {
                        canGVN = 0;
                        return false;
                    }
                }
            }
        }
        canGVN = 1;
        return true;
    }

    public List<IR_FuncBlock> getBeCalledList() {
        return this.becalledlist;
    }

    public List<IR_FuncBlock> getCalledList() {
        return this.calledlist;
    }

    // 计算ptr是否为某个GetEle指令的结果，如果不是，返回null，否则返回该GetEle指令
    public Pointer_Instruction res_From_GetElePtr(IR_Value ptr) {
        for (IR_BaseBlock bb : this.get_bb_List()) {
            for (Instruction instruction : bb.get_Instruction_List()) {
                if (instruction instanceof Pointer_Instruction) {
                    if(ptr.get_ValueName().equals(instruction.get_op().get(0).get_ValueName()))
                    {
                        return (Pointer_Instruction) instruction;
                    }
                }
            }
        }
        return null;
    }

    // 判断ptr是不是函数的参数
    public boolean isParam(IR_Value ptr) {
        for (IR_Value param : params) {
            if (ptr.get_ValueName().equals(param.get_ValueName())){
                return true;
            }
        }
        return false;
    }

    public void replaceUseInFunc(IR_Value oldValue, IR_Value newValue) {
        for (IR_BaseBlock block : this.get_bb_List()) {
            for (Instruction ins : block.get_Instruction_List()) {
                List<IR_Value> op = ins.get_op();

                // 处理phi指令处的引用
                if (ins instanceof Phi_Instruction phiInstruction) {
                    for (IR_BaseBlock key : phiInstruction.get_ComingValues().keySet()) {
                        //自己里面的变化不进行修改
                        if(newValue.equals(phiInstruction.get_op_index(0))){
                            continue;
                        }
                        if (phiInstruction.get_ComingValues().get(key).equals(oldValue)) {
                            phiInstruction.set_ComingValue(newValue, key);
                        }
                    }
                }

                //处理call函数参数处的引用
                if (ins instanceof Call_Instruction callInstruction) {
                    for (int k = 0; k < callInstruction.get_params().size(); k++) {
                        if (callInstruction.get_params().get(k).equals(oldValue)) {
                            callInstruction.setParams(k, newValue);
                        }
                    }
                }

                for (int k = 0; k < op.size(); k++) {
                    if (op.get(k) == null) {
                        continue;
                    }
                    if (op.get(k).get_ValueName().equals(oldValue.get_ValueName())) {
                        if (ins instanceof Pointer_Instruction) {
                            // debug
                            if(k>=2) {
                                ((Pointer_Instruction) ins).set_List(k - 2, newValue);
                            }
                            if(k==1){
                                ((Pointer_Instruction) ins).set_Base(newValue);
                            }
                            ins.set_Op(k, newValue);
                        } else if (ins instanceof Type_Trans_Instruction) {
                            // 对类型转换指令进行特殊的处理
                            if (k == 0) {
                                ((Type_Trans_Instruction) ins).set_Res(newValue);
                            } else if (k == 1) {

                                ((Type_Trans_Instruction) ins).set_Origin(newValue);
                            }
                            ins.set_Op(k, newValue);
                        }
                        else if (ins instanceof Phi_Instruction){
                            continue;
                        }
                        else {
                            ins.set_Op(k, newValue);
                        }
                    }
                }
            }
        }
    }

    public boolean isInLineable() {
        if (get_FuncName().equals("@main")) {
            return false;
        }
        return !this.isLib() && this.calledlist.isEmpty();
    }


    public ArrayList<Instruction> get_UserList_InFunc(IR_Value value) {
        ArrayList<Instruction> users = new ArrayList<>();
        for (IR_BaseBlock block : this.get_bb_List()) {
            for (Instruction ins : block.get_Instruction_List()) {
                List<IR_Value> op = ins.get_op();

                // 处理phi指令处的引用
                if (ins instanceof Phi_Instruction phiInstruction) {
                    for (IR_BaseBlock key : phiInstruction.get_ComingValues().keySet()) {
                        //自己里面的变化不进行修改
                        if(value.equals(phiInstruction.get_op_index(0))){
                            continue;
                        }
                        if (phiInstruction.get_ComingValues().get(key).equals(value)) {
                            users.add(ins);
                        }
                    }
                }

                //处理call函数参数处的引用
                if (ins instanceof Call_Instruction callInstruction) {
                    for (int k = 0; k < callInstruction.get_params().size(); k++) {
                        if (callInstruction.get_params().get(k).equals(value)) {
                            users.add(ins);
                        }
                    }
                }

                for (int k = 0; k < op.size(); k++) {
                    if (op.get(k) == null) {
                        continue;
                    }
                    if (op.get(k).get_ValueName().equals(value.get_ValueName()) && k != 0) {
                        users.add(ins);
                    }
                }
            }
        }
        return users;
    }

    public Alloc_Instrction find_Alloc_Instruciton(IR_Value value) {
        for (IR_BaseBlock block : this.get_bb_List()) {
            for (Instruction ins : block.get_Instruction_List()) {
                List<IR_Value> op = ins.get_op();
                if (ins instanceof Alloc_Instrction && ins.get_op().get(0).equals(value)) {
                    return (Alloc_Instrction) ins;
                }
            }
        }
        return null;
    }

    // 寻找value是哪一条指令的结果
    public Instruction find_Instruction(IR_Value value) {
        for (IR_BaseBlock block : this.get_bb_List()) {
            for (Instruction ins : block.get_Instruction_List()) {
                List<IR_Value> op = ins.get_op();
                if (op.get(0) == null)
                {
                    return null;
                }
                if (ins.get_op().get(0).equals(value)) {
                    return ins;
                }
            }
        }
        return null;
    }

    public void get_CFG() {
        LinkedList<IR_BaseBlock> basicBlocks = this.get_bb_List();
        /*ArrayList<IR_BaseBlock> deletelist = new ArrayList<>();
        for(IR_BaseBlock block : basicBlocks){
            if(block.get_Instruction_List().size() == 0){
                deletelist.add(block);
            }
        }
        for(IR_BaseBlock block : deletelist){
            basicBlocks.remove(block);
        }*/
        if (basicBlocks.size() == 1) {
            preMap.put(basicBlocks.getFirst(), new ArrayList<>());
            nextMap.put(basicBlocks.getFirst(), new ArrayList<>());
            basicBlocks.getFirst().setPreBlock(preMap.get(basicBlocks.getFirst()));
            basicBlocks.getFirst().setNextBlock(nextMap.get(basicBlocks.getFirst()));
            return;
        }
        for (IR_BaseBlock block : basicBlocks) {
            if (preMap.get(block) == null && nextMap.get(block)==null) {
                preMap.put(block, new ArrayList<>());
                nextMap.put(block, new ArrayList<>());
            }
            LinkedList<Instruction> instructions = block.get_Instruction_List();
            Instruction lastInstr = instructions.getLast();
            if (lastInstr instanceof Branch_Instruction && !((Branch_Instruction) lastInstr).br_type()) {
                IR_BaseBlock destBlock = ((Branch_Instruction) lastInstr).get_block1();
                if (preMap.get(destBlock) == null && nextMap.get(destBlock)==null) {
                    preMap.put(destBlock, new ArrayList<>());
                    nextMap.put(destBlock, new ArrayList<>());
                }
                addEdge(block, destBlock);
            }
            else if (lastInstr instanceof Branch_Instruction && ((Branch_Instruction) lastInstr).br_type()) {
                IR_BaseBlock ifTrueBlock = ((Branch_Instruction) lastInstr).get_block1();
                IR_BaseBlock ifFalseBlock = ((Branch_Instruction) lastInstr).get_block2();
                if (preMap.get(ifTrueBlock) == null && nextMap.get(ifTrueBlock)==null) {
                    preMap.put(ifTrueBlock, new ArrayList<>());
                    nextMap.put(ifTrueBlock, new ArrayList<>());
                }
                if (preMap.get(ifFalseBlock) == null && nextMap.get(ifFalseBlock)==null) {
                    preMap.put(ifFalseBlock, new ArrayList<>());
                    nextMap.put(ifFalseBlock, new ArrayList<>());
                }
                addEdge(block, ifTrueBlock);
                addEdge(block, ifFalseBlock);
            }
        }

        for (IR_BaseBlock block : basicBlocks) {
            block.setPreBlock(preMap.get(block));
            block.setNextBlock(nextMap.get(block));
        }
        //function.setPreMap(preMap);
        //function.setNextMap(nextMap);
    }

    private void addEdge(IR_BaseBlock fromBlock, IR_BaseBlock toBlock) {
        nextMap.get(fromBlock).add(toBlock);
        preMap.get(toBlock).add(fromBlock);
    }

    @Override
    public String toString() {
        return get_ValueName();
    }


    public IR_FuncBlock clone() {
        this.cloneMap_Value = new HashMap<>();
        this.cloneMap_Instruction = new HashMap<>();
        IR_FuncBlock function = new IR_FuncBlock(this.Functype, this.get_FuncName());
        for (int i = 0; i < params.size(); i++) {
            //IR_Local_Value new_param = new IR_Local_Value(((IR_Local_Value) param).get_Local_Identity(), param.get_Type());
            //function.add_Param(new_param);
            cloneMap_Value.put(params.get(i), function.get_params().get(i));
        }
        for (IR_BaseBlock block : bbList) {
            IR_BaseBlock copyBlock = new IR_BaseBlock(block.getName(), function);
//            if (copyBlock.getName().equals("block141")) {
//                System.out.println("");
//            }
            function.addbb(copyBlock);
            cloneMap_Value.put(block, copyBlock);
        }

        for (IR_BaseBlock block : bbList) {
            IR_BaseBlock copyBlock = (IR_BaseBlock) cloneMap_Value.get(block);
            for (Instruction instruction : block.get_Instruction_List()) {
                Instruction copyInstr = instruction.clone(copyBlock);
                copyBlock.add_InstructionList(copyInstr);
                if (!(copyInstr instanceof Branch_Instruction && !((Branch_Instruction) copyInstr).br_type())) {
                    // 当指令是br label1 时，op(0)不是结果，不要put
                    cloneMap_Value.put(instruction.get_op().get(0), copyInstr.get_op().get(0));
                }
            }
        }

        function.get_CFG();
//        //TODO:phi指令的前驱基本块
        ArrayList<Phi_Instruction> phiInstrs = new ArrayList<>();
        ArrayList<Alloc_Instrction> allocInstrctions = new ArrayList<>();
        ArrayList<Load_Instruction> loadInstructions = new ArrayList<>();
        ArrayList<Pointer_Instruction> pointerInstructions = new ArrayList<>();
        ArrayList<Calc_Instruction> calcInstructions = new ArrayList<>();
        ArrayList<Type_Trans_Instruction> typeTransInstructions = new ArrayList<>();
        ArrayList<Cond_Instruction> condInstructions = new ArrayList<>();
        for (IR_BaseBlock basicBlock : bbList) {
            for (Instruction instruction : basicBlock.get_Instruction_List()) {
                if (instruction instanceof Phi_Instruction) {
                    phiInstrs.add((Phi_Instruction) instruction);
                }
                else if (instruction instanceof Alloc_Instrction) {
                    allocInstrctions.add((Alloc_Instrction) instruction);
                }
                else if (instruction instanceof Load_Instruction)
                {
                    loadInstructions.add((Load_Instruction) instruction);
                }
                else if (instruction instanceof Pointer_Instruction)
                {
                    pointerInstructions.add((Pointer_Instruction) instruction);
                }
                else if (instruction instanceof Calc_Instruction) {
                    calcInstructions.add((Calc_Instruction) instruction);
                }
                else if (instruction instanceof Type_Trans_Instruction) {
                    typeTransInstructions.add((Type_Trans_Instruction) instruction);
                }
                else if (instruction instanceof Cond_Instruction) {
                    condInstructions.add((Cond_Instruction) instruction);
                }
            }
        }

        // 后面的Alloc指令可能会被前面引用
        for (Alloc_Instrction allocInstrction : allocInstrctions) {
            function.replaceUseInFunc(allocInstrction.get_op().get(0), cloneMap_Value.get(allocInstrction.get_op().get(0)));
        }

        // 后面的load指令可能会被前面引用
        for (Load_Instruction loadInstruction : loadInstructions) {
            function.replaceUseInFunc(loadInstruction.get_op().get(0), cloneMap_Value.get(loadInstruction.get_op().get(0)));
        }

        // 后面的Pointer指令可能会被前面引用
        for (Pointer_Instruction pointerInstruction : pointerInstructions) {
            function.replaceUseInFunc(pointerInstruction.get_op().get(0), cloneMap_Value.get(pointerInstruction.get_op().get(0)));
        }

        // 后面的Calc指令可能会被前面引用
        for (Calc_Instruction calcInstruction : calcInstructions) {
            function.replaceUseInFunc(calcInstruction.get_op().get(0), cloneMap_Value.get(calcInstruction.get_op().get(0)));
        }

        // 后面的Trans指令可能会被前面引用
        for (Type_Trans_Instruction typeTransInstruction : typeTransInstructions) {
            function.replaceUseInFunc(typeTransInstruction.get_op().get(0), cloneMap_Value.get(typeTransInstruction.get_op().get(0)));
        }

        // 后面的Cond指令可能会被前面引用
        for (Cond_Instruction condInstruction : condInstructions) {
            function.replaceUseInFunc(condInstruction.get_op().get(0), cloneMap_Value.get(condInstruction.get_op().get(0)));
        }

        for (Phi_Instruction phiInstr : phiInstrs) {
            // 维护clone生成的phi指令的comingValues
            for (Map.Entry<IR_BaseBlock, IR_Value> entry : phiInstr.get_ComingValues().entrySet()) {
                IR_BaseBlock beforeBlock = entry.getKey();
                IR_BaseBlock nowBlock = (IR_BaseBlock) cloneMap_Value.get(beforeBlock);
                IR_Value beforeValue = entry.getValue();
                IR_Value nowValue = beforeValue;
                if (beforeValue instanceof IR_Int_Value) {
                    nowValue = new IR_Int_Value(((IR_Int_Value) beforeValue).get_Int_Value(), IR_Int32.IR_Int32());
                } else if (beforeValue instanceof IR_Float_Value) {
                    nowValue = new IR_Float_Value(((IR_Float_Value) beforeValue).get_Float_Value(), IR_Float.IR_Float());
                } else {
                    nowValue = cloneMap_Value.get(beforeValue);
                }
//                if (copyInstr.getName().equals("%reg527")) {
//                    System.out.println("");
//                }
                IR_Value phi_res = cloneMap_Value.get(phiInstr.get_op().get(0));
                Phi_Instruction copy_phi = null;
                for (IR_BaseBlock bb : function.bbList) {
                    for (Instruction instruction : bb.get_Instruction_List()) {
                        if (instruction instanceof Phi_Instruction && instruction.get_op().get(0) == phi_res) {
                            copy_phi = (Phi_Instruction) instruction;
                        }
                    }
                }
                copy_phi.set_ComingValue(nowValue, nowBlock);
            }

            // 后面的phi指令结果可能会被前面的指令引用，需要更新一下phi指令的引用
            function.replaceUseInFunc(phiInstr.get_op().get(0), cloneMap_Value.get(phiInstr.get_op().get(0)));
        }




        return function;
    }

    public void setLoopInfo(HashMap<IR_BaseBlock,Loop> loopInfo) {
        this.loopInfo = loopInfo;
    }

    public HashMap<IR_BaseBlock,Loop> getLoopInfo() {
        return loopInfo;
    }

    public void setTopLevelLoops(Set<Loop> topLevelLoops) {
        this.topLevelLoops = topLevelLoops;
    }

    public Set<Loop> getTopLevelLoops() {
        return topLevelLoops;
    }

    public void setLoops(Set<Loop> loops) {
        this.Loops = loops;
    }

    public Set<Loop> getLoops() {
        return Loops;
    }
}
